home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 April: Mac OS SDK / Dev.CD Apr 00 SDK1.toast / Development Kits / Mac OS / Navigation Services SDK / Examples / Sampler / Sampler ƒ / drag.c < prev    next >
Encoding:
Text File  |  1999-06-16  |  23.0 KB  |  826 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        drag.c
  3.  
  4.     Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  5.  
  6. */
  7.  
  8. //    
  9. //    You may incorporate this sample code into your applications
  10. //    without restriction. This sample code has been provided "AS
  11. //    IS" and the responsibility for its operation is 100% yours.
  12. //    You are not permitted to redistribute the source as "Apple
  13. //    sample code" after having made changes. If you're going to
  14. //    re-distribute the source, we require that you make it clear
  15. //    in the source that the code was descended from Apple sample
  16. //    code, but that you've made changes.
  17. //    
  18.  
  19. #pragma segment DocSeg
  20.  
  21. #ifndef __FOLDERS__
  22. #include <Folders.h>
  23. #endif
  24.  
  25. #ifndef __DRAG__
  26. #include <Drag.h>
  27. #endif
  28.  
  29. #ifndef __WINDOWS__
  30. #include <Windows.h>
  31. #endif
  32.  
  33. #ifndef Common_Defs
  34. #include "Common.h"
  35. #endif
  36.  
  37. static long        caretTime;
  38. static short    caretOffset, caretShow, lastOffset, insertPosition, canAcceptItems;
  39. static short    cursorInContent;
  40.  
  41. #define    gCaretTime    ((short)*((long*)0x02F4))    // provides access to TextEdit's caretTime.
  42.  
  43. short HitTest(Point theLoc, Document** theDoc);
  44. void DrawCaret(short offset, TEHandle theTE);
  45. char GetCharAtOffset(short offset, TEHandle theTE);
  46. Boolean WhiteSpace(char theChar);
  47. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE);
  48. void InsertTextAtOffset(short offset, char* theBuf, long size, StScrpHandle theStyl, TEHandle theTE);
  49. short GetSelectionSize(Document* theDocument);
  50. Ptr GetSelectedTextPtr(Document* theDocument);
  51.  
  52. pascal OSErr MySendDataProc(FlavorType theType, void* refCon, ItemReference theItem, DragReference theDrag);
  53. pascal OSErr MyDrawingProc(DragRegionMessage message, RgnHandle showRgn, Point showOrigin, RgnHandle hideRgn, Point hideOrigin, void* dragDrawingRefCon, DragReference theDragRef);
  54. pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon, DragReference theDrag);
  55. pascal OSErr MyTrackingHandler(short message, WindowPtr theWindow, void* handlerRefCon, DragReference theDrag);
  56.  
  57. Boolean DropLocationIsFinderTrash(AEDesc* dropLocation);
  58.  
  59. extern DragSendDataUPP sendHandler;
  60.  
  61.  
  62. // *****************************************************************************
  63. // *
  64. // *    MyDrawingProc()
  65. // *
  66. // *****************************************************************************
  67. pascal OSErr MyDrawingProc(DragRegionMessage message, RgnHandle showRgn, Point /*showOrigin*/, RgnHandle hideRgn, Point /*hideOrigin*/, void* /*dragDrawingRefCon*/, DragReference /*theDragRef*/)
  68. {    
  69.     OSErr        result = paramErr;
  70.     RgnHandle    tempRgn;
  71.  
  72.     switch(message)
  73.         {
  74.         case kDragRegionBegin:
  75.             result = noErr;
  76.             break;
  77.  
  78.         case kDragRegionDraw:
  79.             XorRgn(showRgn,hideRgn,tempRgn = NewRgn());
  80.             InvertRgn(tempRgn);
  81.             DisposeRgn(tempRgn);
  82.             result = noErr;
  83.             break;
  84.  
  85.         case kDragRegionHide:
  86.             InvertRgn(hideRgn);
  87.             result = noErr;
  88.             break;
  89.         }
  90.     return result;
  91. }
  92.  
  93.  
  94. // *****************************************************************************
  95. // *
  96. // *    HitTest()
  97. // *
  98. // *    Given a point in global coordinates, HitTest returns a pointer to a
  99. // *    document structure if the point is inside a document window on the screen.
  100. // *    If the point is not inside a document window, HitTest return NULL in
  101. // *    theDoc. If the point is in a doument window and also in the viewRect of
  102. // *    the document's TextEdit field, HitTest also returns the offset into
  103. // *    the text that corresponds to that point. If the point is not in the text,
  104. // *    HitTest returns -1.
  105. // *
  106. // *****************************************************************************
  107. short HitTest(Point theLoc, Document** theDoc)
  108. {    
  109.     WindowPtr    theWindow;
  110.     short        offset;
  111.  
  112.     *theDoc = 0L;
  113.     offset = -1;
  114.  
  115.     if (FindWindow(theLoc,&theWindow) == inContent)
  116.         {
  117.         if (*theDoc = IsDocumentWindow(theWindow))
  118.             {
  119.             SetPort(theWindow);
  120.             GlobalToLocal(&theLoc);
  121.  
  122.             if ((PtInRect(theLoc, &(**((**theDoc).theTE)).viewRect)) && 
  123.                 (PtInRect(theLoc, &(**((**theDoc).theTE)).destRect)))
  124.                 {
  125.                 offset = TEGetOffset(theLoc,(**theDoc).theTE);
  126.  
  127.                 if ((TEIsFrontOfLine(offset,(**theDoc).theTE)) && (offset) &&            
  128.                         ((*((**((**theDoc).theTE)).hText))[offset - 1] != 0x0D) &&
  129.                         (TEGetPoint(offset - 1,(**theDoc).theTE).h < theLoc.h))
  130.                         {
  131.                         offset--;
  132.                         }
  133.                 }
  134.             }
  135.         }
  136.     return offset;
  137. }
  138.  
  139.  
  140. // *****************************************************************************
  141. // *
  142. // *    DrawCaret()
  143. // *
  144. // *    Draws a caret in a TextEdit field at the given offset. DrawCaret
  145. // *    expects the port to be set to the port that the TextEdit field is in.
  146. // *    DrawCaret inverts the image of the caret onto the screen.
  147. // *
  148. // *****************************************************************************
  149. void DrawCaret(short offset, TEHandle theTE)
  150. {    
  151.     Point    theLoc;
  152.     short    theLine, lineHeight;
  153.  
  154.     // get the coordinates and the line of the offset to draw the caret
  155.     theLoc  = TEGetPoint(offset,theTE);
  156.     theLine = TEGetLine(offset,theTE);
  157.  
  158.     //    For some reason, TextEdit dosen't return the proper coordinates
  159.     //    of the last offset in the field if the last character in the record
  160.     //    is a carriage return. TEGetPoint returns a point that is one line
  161.     //    higher than expected. The following code fixes this problem.
  162.     if ((offset == (**theTE).teLength) && (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0D)
  163.         theLoc.v += TEGetHeight(theLine,theLine,theTE);
  164.  
  165.     PenMode(patXor);                                    //    invert the caret when drawing
  166.     lineHeight = TEGetHeight(theLine,theLine,theTE);    // get the height of the line that the offset points to
  167.  
  168.     MoveTo(theLoc.h - 1,theLoc.v - 1);
  169.     Line(0,1 - lineHeight);                                // draw the appropriate caret image
  170.  
  171.     PenNormal();
  172. }
  173.  
  174.  
  175. // *****************************************************************************
  176. // *
  177. // *    GetCharAtOffset()
  178. // *
  179. // *****************************************************************************
  180. char GetCharAtOffset(short offset, TEHandle theTE)
  181. {
  182.     if (offset < 0)
  183.         return(0x0D);
  184.     return(((char *) *((**theTE).hText))[offset]);
  185. }
  186.  
  187.  
  188. // *****************************************************************************
  189. // *
  190. // *    WhiteSpace()
  191. // *
  192. // *****************************************************************************
  193. Boolean WhiteSpace(char theChar)
  194. {
  195.     return((theChar == ' ') || (theChar == 0x0D));
  196. }
  197.  
  198.  
  199. // *****************************************************************************
  200. // *
  201. // *    WhiteSpaceAtOffset()
  202. // *
  203. // *****************************************************************************
  204. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE)
  205. {    
  206.     char theChar;
  207.  
  208.     if ((offset < 0) || (offset > (**theTE).teLength - 1))
  209.         return(true);
  210.  
  211.     theChar = ((char *) *((**theTE).hText))[offset];
  212.     return((theChar == ' ') || (theChar == 0x0D));
  213. }
  214.  
  215.  
  216. // *****************************************************************************
  217. // *
  218. // *    InsertTextAtOffset()
  219. // *
  220. // *****************************************************************************
  221. void InsertTextAtOffset(short offset, char* theBuf, long size, StScrpHandle theStyl, TEHandle theTE)
  222. {
  223.     if (size == 0)
  224.         return;
  225.  
  226.     //    If inserting at the end of a word and the selection does not begin with
  227.     //    a space, insert a space before the insertion.
  228.     if (!WhiteSpaceAtOffset(offset - 1,theTE) && WhiteSpaceAtOffset(offset,theTE) && !WhiteSpace(theBuf[0]))
  229.         {
  230.         TESetSelect(offset,offset,theTE);
  231.         TEKey(' ',theTE);
  232.         offset++;
  233.         }
  234.  
  235.     //    If inserting at the beginning of a word and the selection does not end
  236.     //    with a space, insert a space after the insertion.
  237.     if (WhiteSpaceAtOffset(offset - 1, theTE) && !WhiteSpaceAtOffset(offset, theTE) && !WhiteSpace(theBuf[size - 1]))
  238.         {
  239.         TESetSelect(offset, offset,theTE);
  240.         TEKey(' ',theTE);
  241.         }
  242.  
  243.     TESetSelect(offset,offset,theTE);
  244.     TEStyleInsert(theBuf,size,theStyl,theTE);
  245.     TESetSelect(offset,offset + size,theTE);
  246. }
  247.  
  248.  
  249. // *****************************************************************************
  250. // *
  251. // *    GetSelectionSize()
  252. // *
  253. // *****************************************************************************
  254. short GetSelectionSize(Document* theDocument)
  255. {
  256.     return((**(theDocument->theTE)).selEnd - (**(theDocument->theTE)).selStart);
  257. }
  258.  
  259.  
  260. // *****************************************************************************
  261. // *
  262. // *    GetSelectedTextPtr()
  263. // *
  264. // *****************************************************************************
  265. Ptr GetSelectedTextPtr(Document* theDocument)
  266. {
  267.     return((*(**(theDocument->theTE)).hText) + (**(theDocument->theTE)).selStart);
  268. }
  269.  
  270.  
  271. // *****************************************************************************
  272. // *
  273. // *    MySendDataProc()
  274. // *
  275. // *    Will provide 'styl' data for the drag when requested.
  276. // *
  277. // *****************************************************************************
  278. pascal OSErr MySendDataProc(FlavorType theType, void* refCon, ItemReference theItem, DragReference theDrag)
  279. {    
  280.     Document*        theDocument = (Document*)refCon;
  281.     StScrpHandle    theStyl;
  282.  
  283.     if (theType == 'styl') 
  284.         {
  285.         theStyl = TEGetStyleScrapHandle(theDocument->theTE);
  286.  
  287.         // Call SetDragItemFlavorData to provide the requested data.
  288.         HLock((Handle) theStyl);
  289.         SetDragItemFlavorData(theDrag,theItem,'styl',(Ptr)*theStyl,GetHandleSize((Handle)theStyl),0L);
  290.         HUnlock((Handle)theStyl);
  291.         DisposeHandle((Handle)theStyl);
  292.         } 
  293.     else
  294.         return badDragFlavorErr;
  295.  
  296.     return noErr;
  297. }
  298.  
  299.  
  300. // *****************************************************************************
  301. // *
  302. // *    MyReceiveDropHandler()
  303. // *
  304. // *****************************************************************************
  305. pascal OSErr MyReceiveDropHandler(WindowPtr theWindow, unsigned long handlerRefCon, DragReference theDrag)
  306. {    
  307.     OSErr                result;
  308.     TEHandle            tempTE;
  309.     Rect                theRect, srcRect;
  310.     unsigned short        items, index;
  311.     ItemReference        theItem;
  312.     DragAttributes        attributes;
  313.     Ptr                    textData;
  314.     StScrpHandle        stylHandle;
  315.     Size                textSize, stylSize;
  316.     short                offset, selStart, selEnd, mouseDownModifiers, mouseUpModifiers, moveText;
  317.     Document*            theDocument = (Document*)handlerRefCon;
  318.     Point                thePoint;
  319.  
  320.     if ((!canAcceptItems) || (insertPosition == -1))
  321.         return(dragNotAcceptedErr);
  322.  
  323.     SetPort(theWindow);
  324.  
  325.     GetDragAttributes(theDrag, &attributes);
  326.     GetDragModifiers(theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers);
  327.  
  328.     moveText = (attributes & kDragInsideSenderWindow) &&
  329.                (!((mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey)));
  330.  
  331.     //    Loop through all of the drag items contained in this drag and collect the text
  332.     //    into the tempTE record.
  333.     
  334.     SetRect(&theRect,0,0,0,0);
  335.     tempTE = TEStyleNew(&theRect,&theRect);
  336.  
  337.     CountDragItems(theDrag, &items);
  338.  
  339.     for (index = 1; index <= items; index++)
  340.         {
  341.         GetDragItemReferenceNumber(theDrag, index, &theItem);
  342.  
  343.         //    get the flags for a 'TEXT' flavor. If this returns noErr,
  344.         //    then we know that a 'TEXT' flavor exists in the item.
  345.  
  346.         result = GetFlavorDataSize(theDrag, theItem,'TEXT',&textSize);
  347.  
  348.         if (result == noErr) 
  349.             {
  350.             textData = NewPtr(textSize);
  351.             if (textData == 0L)
  352.                 {
  353.                 TEDispose(tempTE);
  354.                 return(memFullErr);
  355.                 }
  356.  
  357.             GetFlavorData(theDrag,theItem,'TEXT',textData,&textSize,0L);
  358.  
  359.             // check for optional styl data for the TEXT.
  360.  
  361.             stylHandle = 0L;
  362.             result = GetFlavorDataSize(theDrag,theItem,'styl',&stylSize);
  363.             if (result == noErr)
  364.                 {
  365.                 stylHandle = (StScrpHandle)NewHandle(stylSize);
  366.                 if (stylHandle == 0L)
  367.                     {
  368.                     TEDispose(tempTE);
  369.                     DisposePtr(textData);
  370.                     return(memFullErr);
  371.                     }
  372.  
  373.                 HLock((Handle)stylHandle);
  374.                 GetFlavorData(theDrag,theItem,'styl',*stylHandle,&stylSize,0L);
  375.                 HUnlock((Handle)stylHandle);
  376.                 }
  377.  
  378.             // insert this drag item's text into the tempTE
  379.  
  380.             TESetSelect(32767,32767,tempTE);
  381.             TEStyleInsert(textData,textSize,stylHandle,tempTE);
  382.  
  383.             DisposePtr(textData);
  384.             if (stylHandle)
  385.                 DisposeHandle((Handle) stylHandle);
  386.             }
  387.         }
  388.  
  389.     // pull the TEXT and styl data out of the tempTE handle.
  390.  
  391.     textData = NewPtr(textSize = (**tempTE).teLength);
  392.     if (textData == 0L)
  393.         {
  394.         TEDispose(tempTE);
  395.         return(memFullErr);
  396.         }
  397.     BlockMove(*(**tempTE).hText,textData,textSize);
  398.  
  399.     TESetSelect(0,32767,tempTE);
  400.     stylHandle = TEGetStyleScrapHandle(tempTE);
  401.  
  402.     TEDispose(tempTE);
  403.  
  404.     // if we actually received text, insert it into the destination
  405.  
  406.     if (textSize != 0)
  407.         {
  408.         // if the caret or highlighting is on the screen, remove it/them
  409.  
  410.         offset = caretOffset;
  411.  
  412.         if (caretOffset != -1)
  413.             {
  414.             DrawCaret(caretOffset,theDocument->theTE);
  415.             caretOffset = -1;
  416.             }
  417.  
  418.         if (attributes & kDragHasLeftSenderWindow)
  419.             HideDragHilite(theDrag);
  420.  
  421.         //    If the drag occurred completely within the same window and the window is not
  422.         //    frontmost, bring the window forward and update its contents before completing
  423.         //    the drag.
  424.  
  425.         if ((attributes & kDragInsideSenderWindow) && (theDocument->theWindow != FrontWindow()))
  426.             {
  427.             SelectWindow(theDocument->theWindow);
  428.             UpdateWindow(theDocument);
  429.             TEActivate(theDocument->theTE);
  430.             }
  431.  
  432.         // if the window is not active, must activate TE before inserting
  433.         // text or the background hilite will not update correctly.
  434.  
  435.         if (!((WindowPeek) theDocument->theWindow)->hilited)
  436.             if (!IsWindowHilited(theDocument->theWindow))
  437.                 TEActivate(theDocument->theTE);
  438.  
  439.         // if this window is also the sender, delete source selection if no option key.
  440.         if (moveText)
  441.             {
  442.             selStart = (**(theDocument->theTE)).selStart;
  443.             selEnd   = (**(theDocument->theTE)).selEnd;
  444.             if ( WhiteSpaceAtOffset(selStart - 1, theDocument->theTE) &&
  445.                 !WhiteSpaceAtOffset(selStart, theDocument->theTE) &&
  446.                 !WhiteSpaceAtOffset(selEnd - 1, theDocument->theTE) &&
  447.                  WhiteSpaceAtOffset(selEnd, theDocument->theTE))
  448.                 {
  449.                 if (GetCharAtOffset(selEnd, theDocument->theTE) == ' ')
  450.                     (**(theDocument->theTE)).selEnd++;
  451.                 }
  452.             if (insertPosition > selStart)
  453.                 {
  454.                 insertPosition -= ((**(theDocument->theTE)).selEnd -
  455.                                    (**(theDocument->theTE)).selStart);
  456.                 }
  457.             srcRect = (**theDocument->hiliteRgn).rgnBBox;
  458.             TEDelete(theDocument->theTE);
  459.             }
  460.  
  461.         InsertTextAtOffset(insertPosition,textData,textSize,stylHandle,theDocument->theTE);
  462.  
  463.         TEGetHiliteRgn(theDocument->hiliteRgn, theDocument->theTE);
  464.  
  465.         //    If the text is moving (not copying) within the same window, provide a ZoomRects
  466.         //    from the source to the destination before revealing the reflowed text.
  467.  
  468.         if (moveText)
  469.             {
  470.             theRect = (**theDocument->hiliteRgn).rgnBBox;
  471.             thePoint.h = thePoint.v = 0;
  472.             
  473.             SetPort(theWindow);
  474.  
  475.             LocalToGlobal(&thePoint);
  476.             OffsetRect(&srcRect,thePoint.h,thePoint.v);
  477.             OffsetRect(&theRect,thePoint.h,thePoint.v);
  478.             ZoomRects(&srcRect,&theRect,12,kZoomDecelerate);
  479.             }
  480.         theDocument->dirty = true;
  481.         }
  482.  
  483.     DisposePtr(textData);
  484.  
  485.     if (stylHandle)
  486.         DisposeHandle((Handle)stylHandle);
  487.  
  488.     // undo the TEActivate, if needed
  489.     if (!IsWindowHilited(theDocument->theWindow))
  490.         if (!((WindowPeek) theDocument->theWindow)->hilited)
  491.             TEDeactivate(theDocument->theTE);
  492.  
  493.     (**(theDocument->theTE)).inPort = (GrafPtr)theDocument->theWindow;
  494.  
  495.     return noErr;
  496. }
  497.  
  498.  
  499. // *****************************************************************************
  500. // *
  501. // *    MyTrackingHandler()
  502. // *
  503. // *    This is the drag tracking handler for windows in the application.
  504. // *
  505. // *****************************************************************************
  506. pascal OSErr MyTrackingHandler(short message, WindowPtr /*theWindow*/, void* handlerRefCon, DragReference theDrag)
  507. {    
  508.     short                result, offset;
  509.     long                theTime = TickCount();
  510.     unsigned short        count, index;
  511.     unsigned long        flavorFlags, attributes;
  512.     ItemReference        theItem;
  513.     RgnHandle            theRgn;
  514.     Document*            theDocument = (Document*)handlerRefCon;
  515.     Document*            hitDoc;
  516.     Point                theMouse, localMouse;
  517.  
  518.     if ((message != kDragTrackingEnterHandler) && (!canAcceptItems))
  519.         return(noErr);
  520.  
  521.     GetDragAttributes(theDrag, &attributes);
  522.  
  523.     switch (message)
  524.         {
  525.         case kDragTrackingEnterHandler:
  526.  
  527.             //    We get called with this message the first time that a drag enters ANY
  528.             //    window in our application. Check to see if all of the drag items contain
  529.             //    TEXT. We only accept a drag if all of the items in the drag can be accepted.
  530.             
  531.             canAcceptItems = true;
  532.  
  533.             CountDragItems(theDrag, &count);
  534.  
  535.             for (index = 1; index <= count; index++)
  536.                 {
  537.                 GetDragItemReferenceNumber(theDrag,index,&theItem);
  538.  
  539.                 result = GetFlavorFlags(theDrag, theItem,'TEXT',&flavorFlags);
  540.  
  541.                 if (result != noErr)
  542.                     {
  543.                     canAcceptItems = false;
  544.                     break;
  545.                     }
  546.                 }
  547.             break;
  548.  
  549.         case kDragTrackingEnterWindow:
  550.  
  551.             //    We receive an EnterWindow message each time a drag enters one of our
  552.             //    application's windows. We initialize our global variables for tracking
  553.             //    the drag through the window.
  554.  
  555.             caretTime = theTime;
  556.             caretOffset = lastOffset = -1;
  557.             caretShow = true;
  558.  
  559.             cursorInContent = false;
  560.  
  561.             break;
  562.  
  563.         case kDragTrackingInWindow:
  564.  
  565.             //    We receive InWindow messages as long as the mouse is in one of our windows
  566.             //    during a drag. We draw the window highlighting and blink the insertion caret
  567.             //    when we get these messages.
  568.  
  569.             GetDragMouse(theDrag,&theMouse,0L);
  570.             localMouse = theMouse;
  571.             GlobalToLocal(&localMouse);
  572.  
  573.             //    Show or hide the window highlighting when the mouse enters or leaves the
  574.             //    TextEdit field in our window (we don't want to show the highlighting when
  575.             //    the mouse is over the window title bar or over the scroll bars).
  576.  
  577.             if (attributes & kDragHasLeftSenderWindow) 
  578.                 {
  579.                 if (PtInRect(localMouse, &(**(theDocument->theTE)).viewRect))
  580.                     {
  581.                     if (!cursorInContent)
  582.                         {
  583.                         RectRgn(theRgn = NewRgn(),&(**(theDocument->theTE)).viewRect);
  584.                         ShowDragHilite(theDrag,theRgn,true);
  585.                         DisposeRgn(theRgn);
  586.                         }
  587.                     cursorInContent = true;
  588.                     }
  589.                 else
  590.                     {
  591.                     if (cursorInContent)
  592.                         HideDragHilite(theDrag);
  593.                     cursorInContent = false;
  594.                     }
  595.                 }
  596.  
  597.             offset = HitTest(theMouse, &hitDoc);
  598.  
  599.             //    If this application is the sender, do not allow tracking through
  600.             //    the selection in the window that sourced the drag.
  601.  
  602.             if (attributes & kDragInsideSenderWindow)
  603.                 {
  604.                 if ((offset >= (**(theDocument->theTE)).selStart) &&
  605.                     (offset <= (**(theDocument->theTE)).selEnd))
  606.                         offset = -1;
  607.                 }
  608.  
  609.             if (hitDoc == theDocument)
  610.                 {
  611.                 insertPosition = offset;
  612.  
  613.                 //    Reset flashing counter if the offset has moved. This makes the
  614.                 //    caret blink only after the caret has stopped moving long enough.
  615.  
  616.                 if (offset != lastOffset)
  617.                     {
  618.                     caretTime = theTime;
  619.                     caretShow = true;
  620.                     }
  621.                 lastOffset = offset;
  622.  
  623.                 // flash caret
  624.  
  625.                 if (theTime - caretTime > gCaretTime)
  626.                     {
  627.                     caretShow = !caretShow;
  628.                     caretTime = theTime;
  629.                     }
  630.                 if (!caretShow)
  631.                     offset = -1;
  632.  
  633.                 // if caret offset has changed, move caret on screen
  634.  
  635.                 if (offset != caretOffset)
  636.                     {
  637.                     if (caretOffset != -1)
  638.                         {
  639.                         DrawCaret(caretOffset,theDocument->theTE);
  640.                         }
  641.                     if (offset != -1)
  642.                         {
  643.                         DrawCaret(offset,theDocument->theTE);
  644.                         }
  645.                     }
  646.  
  647.                 caretOffset = offset;
  648.                 }
  649.             else 
  650.                 {
  651.                 lastOffset = offset;
  652.                 insertPosition = -1;
  653.                 }
  654.             break;
  655.  
  656.         case kDragTrackingLeaveWindow:
  657.  
  658.             // if the caret is on the screen, remove it.
  659.  
  660.             if (caretOffset != -1)
  661.                 {
  662.                 DrawCaret(caretOffset,theDocument->theTE);
  663.                 caretOffset = -1;
  664.                 }
  665.  
  666.             // remove window highlighting, if showing.
  667.  
  668.             if ((cursorInContent) && (attributes & kDragHasLeftSenderWindow))
  669.                 HideDragHilite(theDrag);
  670.             break;
  671.  
  672.         case kDragTrackingLeaveHandler:
  673.             break;
  674.  
  675.         }
  676.     return noErr;
  677. }
  678.  
  679.  
  680. // *****************************************************************************
  681. // *
  682. // *    DropLocationIsFinderTrash()
  683. // *
  684. // *    Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
  685. // *
  686. // *****************************************************************************
  687. Boolean DropLocationIsFinderTrash(AEDesc* dropLocation)
  688. {    
  689.     OSErr        result;
  690.     AEDesc        dropSpec;
  691.     FSSpec*        theSpec;
  692.     CInfoPBRec    thePB;
  693.     short        trashVRefNum;
  694.     long        trashDirID;
  695.  
  696.     //    Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or
  697.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  698.  
  699.     if ((dropLocation->descriptorType != typeNull) &&
  700.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr))
  701.         {
  702.         HLock(dropSpec.dataHandle);
  703.         theSpec = (FSSpec*)*dropSpec.dataHandle;
  704.  
  705.         // get the directory ID of the given dropLocation object
  706.         thePB.dirInfo.ioCompletion = 0L;
  707.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  708.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  709.         thePB.dirInfo.ioFDirIndex = 0;
  710.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  711.  
  712.         result = PBGetCatInfoSync(&thePB);
  713.  
  714.         HUnlock(dropSpec.dataHandle);
  715.         AEDisposeDesc(&dropSpec);
  716.  
  717.         if (result != noErr)
  718.             return(false);
  719.  
  720.         // if the result is not a directory, it must not be the Trash.
  721.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  722.             return false;
  723.  
  724.         // get information about the Trash folder
  725.         FindFolder(theSpec->vRefNum,kTrashFolderType,kCreateFolder,&trashVRefNum,&trashDirID);
  726.  
  727.         //    if the directory ID of the dropLocation object is the same as the directory ID
  728.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  729.  
  730.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  731.             return true;
  732.         }
  733.     return false;
  734. }
  735.  
  736.  
  737. // *****************************************************************************
  738. // *
  739. // *    DragText()
  740. // *
  741. // *****************************************************************************
  742. short DragText(Document* theDocument, EventRecord* theEvent, RgnHandle hiliteRgn)
  743. {    
  744.     OSErr                theErr = noErr;
  745.     short                result;
  746.     RgnHandle            dragRegion, tempRgn;
  747.     Point                theLoc;
  748.     DragReference        theDrag;
  749.     StScrpHandle        theStyl;
  750.     AEDesc                dropLocation;
  751.     DragAttributes        attributes;
  752.     short                mouseDownModifiers, mouseUpModifiers, copyText;
  753.  
  754.     // copy the hilite region into dragRegion and offset it into global coordinates.
  755.     CopyRgn(hiliteRgn,dragRegion = NewRgn());
  756.     SetPt(&theLoc,0,0);
  757.     LocalToGlobal(&theLoc);
  758.     OffsetRgn(dragRegion,theLoc.h,theLoc.v);
  759.  
  760.     if (!WaitMouseMoved(theEvent->where))
  761.         return false;
  762.         
  763.     NewDrag(&theDrag);
  764.  
  765.     AddDragItemFlavor(theDrag,1,'TEXT',GetSelectedTextPtr(theDocument),GetSelectionSize(theDocument),0);
  766.  
  767.     theStyl = TEGetStyleScrapHandle(theDocument->theTE);
  768.     HLock((Handle) theStyl);
  769.     AddDragItemFlavor(theDrag,1,'styl',(Ptr)*theStyl,GetHandleSize((Handle)theStyl),0);
  770.     HUnlock((Handle) theStyl);
  771.     DisposeHandle((Handle)theStyl);
  772.  
  773.     sendHandler = NewDragSendDataProc(&MySendDataProc);
  774.     theErr = SetDragSendProc(theDrag,sendHandler,(void*)theDocument);
  775.  
  776.     SetDragItemBounds(theDrag,1,&(**dragRegion).rgnBBox);
  777.  
  778.     // prepare the drag region
  779.     tempRgn = NewRgn();
  780.     CopyRgn(dragRegion,tempRgn);
  781.     InsetRgn(tempRgn,1,1);
  782.     DiffRgn(dragRegion,tempRgn,dragRegion);
  783.     DisposeRgn(tempRgn);
  784.  
  785.     /*
  786.     // use this if you want custom draw outlines..
  787.     if (gCanDrag)
  788.         {
  789. #if USESROUTINEDESCRIPTORS
  790.         sendHandler = NewDragSendDataProc(&MyDrawingProc);
  791.         theErr = SetDragDrawingProc(theDrag,drawHandler,0L);
  792. #else
  793.         theErr = SetDragDrawingProc(theDrag,MyDrawingProc,0L);
  794. #endif
  795.         }
  796.     */
  797.  
  798.     result = TrackDrag(theDrag,theEvent,dragRegion);
  799.  
  800.     if (result != noErr && result != userCanceledErr)
  801.         return true;
  802.  
  803.     // check to see if the drop occurred in the Finder's Trash. If the drop occurred
  804.     // in the Finder's Trash and a copy operation wasn't specified, delete the source selection
  805.  
  806.     GetDragAttributes(theDrag,&attributes);
  807.     if (!(attributes & kDragInsideSenderApplication))
  808.         {
  809.         GetDropLocation(theDrag,&dropLocation);
  810.  
  811.         GetDragModifiers(theDrag,0L,&mouseDownModifiers,&mouseUpModifiers);
  812.         copyText = (mouseDownModifiers | mouseUpModifiers) & optionKey;
  813.  
  814.         if ((!copyText) && (DropLocationIsFinderTrash(&dropLocation)))
  815.             {
  816.             TEDelete(theDocument->theTE);
  817.             theDocument->dirty = true;
  818.             }
  819.         AEDisposeDesc(&dropLocation);
  820.         }
  821.  
  822.     DisposeDrag(theDrag);
  823.     DisposeRgn(dragRegion);
  824.  
  825.     return true;
  826. }